from otree.api import * author: 'Nathaniel Lawrence, LEMMA, Université Panthéon-Assas' doc = """ This app provides a consumption simultion app, combined with surveys on demographics; financial knowledge, behavior, and attitude; and numeracy; as well as tests of time preferences and compulsivity/learning rates. Shopping cart feature developed from Shopping app (online grocery store, oTree - https://s3.amazonaws.com/otreehub/browsable_source/d7188187-cdba-4c61-ae3a-d7cc3d6fcde9/shop/index.html) """ ### csv reader function def read_csv(): import csv f = open(__name__ + '/catalog.csv', encoding='utf-8-sig') rows = [row for row in csv.DictReader(f, delimiter=";")] for row in rows: # all values in CSV are string unless you convert them row['unit_price'] = cu(row['unit_price']) return rows def read_csv_balances(): import csv f = open(__name__ + '/balances.csv', encoding='utf-8-sig') rows = [row for row in csv.DictReader(f, delimiter=";")] for row in rows: # all values in CSV are string unless you convert them row['unit_price'] = cu(row['unit_price']) ### Define constants here, in all-caps class C(BaseConstants): NAME_IN_URL = '' PLAYERS_PER_GROUP = None NUM_ROUNDS = 3 ### price changes occur within a block of n periods #BLOCK_SIZE = 5 #NUM_BLOCKS = 6 ### should be greater than BLOCK_SIZE * NUM_BLOCKS #NUM_TRIALS = 30 ### consumption constants INITIAL_ENDOWMENT = cu(11.97) INITIAL_PRICE = cu(3.99) INCOME = cu(3.99) INTEREST_RATE = 0.1 INFLATION_RATE_CHRONIC = 0.2 INFLATION_RATE_ACUTE = 0.8 CONSUMPTION_RATE = 1 ### the rate at which goods are consumed per period PRODUCTS = read_csv() ### SKU = 'stock keeping unit' = product ID PRODUCTS_DICT = {row['sku']: row for row in PRODUCTS} class Subsession(BaseSubsession): pass # def generateInitialBalances(): # return dict(initialSavings=C.INITIAL_ENDOWMENT, cashOnHand=C.INITIAL_ENDOWMENT+C.INCOME,finalSavings='',finalStock='') def createSession(subsession: Subsession): session = subsession.session class Group(BaseGroup): pass ### define the questions a player must answer here class Player(BasePlayer): total_price = models.CurrencyField(initial=0) initialSavings = models.CurrencyField(label='Initial savings: ', initial=C.INITIAL_ENDOWMENT ) cashOnHand = models.CurrencyField(initial=0) # finalSavings = models.CurrencyField(initial=0) # finalStock = models.IntegerField(initial=0) def calcCashOnHand(player: Player): return player.initialSavings + C.INCOME # def calcFinalSavings(balance: Balances): # return balance.cashOnHand + balance.cashOnHand - player.total_price # def calcInitialSavings(balance: Balances): # return C.INITIAL_ENDOWMENT ##quantity = models.IntegerField(label='Quantity to purchase', min = 0) class Item(ExtraModel): player = models.Link(Player) sku = models.StringField() name = models.StringField() quantity = models.IntegerField() unit_price = models.CurrencyField() # initialSavings = models.CurrencyField() # class Balances(ExtraModel): # player = models.Link(Player) # initialSavings = models.CurrencyField(initial=C.INITIAL_ENDOWMENT) # cashOnHand = models.CurrencyField() # finalSavings = models.CurrencyField() # finalStock = models.IntegerField() # Functions def total_price(item: Item): return item.quantity * item.unit_price def to_dict(item: Item): return dict( sku=item.sku, name=item.name, quantity=item.quantity, total_price=total_price(item), ) # def to_dict_balances(balance: Balances): # return dict( # initialSavings=calcInitialSavings, cashOnHand=calcCashOnHand(cashOnHand), finalSavings=calcFinalSavings(finalSavings), stockBalance=balance.stockBalance, # ) def live_method(player: Player, data): if 'sku' in data: sku = data['sku'] delta = data['delta'] product = C.PRODUCTS_DICT[sku] matches = Item.filter(player=player, sku=sku) print(data) if matches: [item] = matches item.quantity += delta if item.quantity <= 0: item.delete() else: if delta > 0: Item.create( player=player, quantity=delta, sku=sku, name=product['name'], unit_price=product['unit_price'], ) items = Item.filter(player=player) print("Item",items) print(bool(items)) item_dicts = [to_dict(item) for item in items] player.total_price = sum([total_price(item) for item in items]) return {player.id_in_group: dict(items=item_dicts, total_price=player.total_price)} #stockBalance = item.quantity - C.CONSUMPTION_RATE # if bool(items) == true: # Balances.create( # player=player, # initialSavings=initialSavings, # cashOnHand=initialSavings + C.INCOME, # finalSavings=cashOnHand - player.total_price, # stockBalance=stockBalance + item.quantity - C.CONSUMPTION_RATE, # ) # print("Balances",balances) # balances = Balances.filter(player=player) # balances_dicts = [to_dict_balances(balance) for balance in balances] # # player.calcCashOnHand = # return {player.id_in_group: dict(balances=balances_dicts)} # Functions ### function to calculate first period's initial savings balance #def savingsInitial_1(): # return C.INITIAL_ENDOWMENT ### calculate cash on hand in first period #def cashOnHand_1(): # return savingsInitial_1(player) + C.INCOME ### calculate total spent per subject's quantity selection in first period #def totalSpent_1(quantity): # return player.quantity * C.INITIAL_PRICE ### final savings balance per subjects' quantity selection in first period #def savingsFinal_1(): # return cashOnHand_1(player) - totalSpent_1(player) ### calculate stock balance after consumption of the given period #def stockBalance_1(): # return player.quantity * C.CONSUMPTION_RATE ### function to calculate following period's initial savings balance #def savingsInitial_t(): # return savin # PAGES class t1(Page): live_method = live_method # form_model = 'player' # form_fields = ['initialSavings','cashOnHand'] #class t2(Page): # live_method = live_method #form_model = 'player' #form_fields = ['quantity'] # @staticmethod # def vars_for_template(player): # totalCash_1 = C. # return dict( # totalSpent = totalSpent # ) #@staticmethod #def is_diplayed(player): # return player ######simulationTask = simulationTask #class ResultsWaitPage(WaitPage): # pass class Results(Page): @staticmethod def vars_for_template(player: Player): return dict(items=Item.filter(player=player)) return dict(balances=Balances.filter(player=player)) page_sequence = [t1, Results]